import {
  Ref,
  forwardRef,
  useEffect,
  useImperativeHandle,
  useState,
} from "react";
import "./style/DesignerGlobalStyle.less";
import DesignerLeft from "./left/DesignerLeft";
import DesignerRight from "./right/DesignerRight";
import DesignerFooter from "./footer/DesignerFooter";
import contextMenuStore from "./operate-provider/canvas-context-menu/CanvasContextMenuStore";
import eventOperateStore from "./operate-provider/EventOperateStore";
import DesignerHeader from "./header/DesignerHeader";
import DesignerCanvas from "./canvas/DesignerCanvas";
import { observer } from "mobx-react";
import Loading from "../json-schema/ui/loading/Loading";
import DesignerLoaderFactory from "./loader/DesignerLoaderFactory";
import FrameLayout from "../json-schema/ui/frame-layout/FrameLayout";
import { DesignerMode, IProjectInfo, SaveType } from "./DesignerType";
import designerManager from "./manager/DesignerManager";
import "../designer/resource/font/FontGlobal.css";
import operatorMap from "framework/operate";
import FileUtil from "utils/FileUtil";
import { DesignerProvider } from "components/common/DesignerContext";
import { AxiosInstance } from "axios";
import { MessagePlugin } from "tdesign-react";

/**
 * 绑定事件到dom元素
 */
function bindEventToDom() {
  document.addEventListener("click", clickHandler);
  document.addEventListener("contextmenu", contextMenuHandler);
  document.addEventListener("pointerdown", pointerDownHandler);
  document.addEventListener("pointerup", pointerUpHandler);
}

/**
 * 卸载dom元素上的事件
 */
function unbindEventToDom() {
  document.removeEventListener("click", clickHandler);
  document.removeEventListener("contextmenu", contextMenuHandler);
  document.removeEventListener("pointerdown", pointerDownHandler);
  document.removeEventListener("pointerup", pointerUpHandler);
}

/*****************事件处理*****************/
const clickHandler = (event: MouseEvent) => {
  const { visible, updateVisible } = contextMenuStore;
  if (visible && event.button === 0) {
    //这里添加异步处理的原因：必须要在操作菜单执行点击事件执行之后才能卸载dom元素，不然操作菜单的点击事件会失效。
    const tempTimer = setTimeout(() => {
      updateVisible(false);
      clearTimeout(tempTimer);
    });
  }
};

const contextMenuHandler = (event: MouseEvent) => {
  event.preventDefault();
  const { mouseDownTime, mouseUpTime, setPosition, updateVisible } =
    contextMenuStore;
  const { targetIds } = eventOperateStore;
  //由于mac端下 setPointerCapture(e.pointerId); 会导致事件target指向发生变化，进而导致右键菜单失效，因此调整为只要选中了元素，且在主画布范围内均可显示右键菜单
  const eventContainer = document.querySelector(".lc-event-container");
  const layerListContainer = document.querySelector(".layer-items");
  if (
    targetIds &&
    targetIds.length > 0 &&
    (eventContainer?.contains(event.target as HTMLElement) ||
      layerListContainer?.contains(event.target as HTMLElement)) &&
    mouseUpTime - mouseDownTime < 200
  ) {
    updateVisible && updateVisible(true);
    setPosition([event.clientX, event.clientY]);
  } else {
    updateVisible && updateVisible(false);
  }
};

const pointerDownHandler = () => {
  const { setMouseDownTime } = contextMenuStore;
  setMouseDownTime(Date.now());
};

const pointerUpHandler = (event: PointerEvent) => {
  const { setMouseUpTime } = contextMenuStore;
  setMouseUpTime(Date.now());
  const { setPointerTarget } = eventOperateStore;
  setPointerTarget(event.target as HTMLElement);
};

export interface DesignerProps {
  facilityId?: number;
  /**
   * 预览项目
   * @param id 项目id
   * @param type
   * @returns
   */
  onPreview?: (id: string, type: SaveType) => void;

  /**
   * 保存项目
   * @param id 项目id
   * @param json 场景json
   * @returns
   */
  onSave?: (id: string, json: string) => void;
  onExport?: (id: string, json: string) => void;

  /**
   * axios配置
   */
  axios?: AxiosInstance;

  // 产品分类编码,用来过滤组件
  productCodes?: number[];
}

export interface DesignerInstance {
  /**
   * 创建场景项目
   * @param name 场景名称
   * @param desc 场景描述
   * @returns 项目id
   */
  create: (name: string, facilityId?: string) => Promise<string>;
  /**
   * 加载场景项目
   * @param dataJson 项目json
   * @returns
   */
  load: (dataJson: string) => void;
  // 销毁
  destroy: () => void;
}

const Designer = forwardRef(
  (props: DesignerProps, ref: Ref<DesignerInstance>) => {
    const { onSave, onPreview, onExport, axios, facilityId, productCodes } =
      props;
    const staticType = SaveType.LOCAL; // 只在本地存储
    const [projectId, setProjectId] = useState<string>();
    useImperativeHandle(ref, () => ({
      create: (name) => createDesigner(name),
      load: (dataJson) => loadProject(dataJson),
      destroy: () => {
        designerManager.destroy();
      },
    }));

    useEffect(() => {
      return () => {
        unbindEventToDom(); //卸载dom元素上的事件
      };
    }, []);

    // 初始化项目
    const initialization = async (project: IProjectInfo) => {
      const pid = await operatorMap[staticType].createProject(project);
      bindEventToDom();
      const _time = setTimeout(() => {
        //加载设计器
        DesignerLoaderFactory.getLoader(DesignerMode.EDIT).load(
          pid,
          staticType
        );
        setProjectId(pid);

        //绑定事件到dom元素
        clearTimeout(_time);
      }, 1000);
      return pid;
    };

    /**
     * 创建设计器
     * @param name 场景名称
     * @param desc 场景描述
     * @returns 场景id
     */
    const createDesigner = async (name: string) => {
      unbindEventToDom(); //卸载dom元素上的事件
      const project: IProjectInfo = {
        name,
        des: "",
        saveType: staticType,
        dataJson: JSON.stringify({
          canvasManager: { width: 1920, height: 1080 },
        }),
      };
      return await initialization(project);
    };

    const loadProject = async (dataJson: string) => {
      designerManager.destroy();
      // 加载数据前先初始化数据
      const importData = JSON.parse(dataJson);
      // 初始化
      const projectData = importData.data;
      const elemConfigs = projectData?.layerManager?.elemConfigs;
      const promises: Promise<void>[] = [];
      if (elemConfigs) {
        Object.keys(elemConfigs).forEach((key) => {
          const item = elemConfigs[key];
          if (
            item.base.type === "BaseImage" &&
            (item.style.localUrl as string)?.startsWith("blob")
          ) {
            // 将 blob 数据转换为 base64，并将异步操作添加到 promises 数组中
            promises.push(
              FileUtil.base64ToBlob(item.style.localUrl as string).then(
                (res: string | boolean) => {
                  if (res) item.style.localUrl = res;
                  else {
                    console.error(
                      `${item.base.id + "_" + item.base.name} 图片blob转换失败, ${item.style.localUrl}`
                    );
                  }
                }
              )
            );
          }
        });
      }
      await Promise.all(promises);
      // console.log("projectData:", projectData);
      //designerManager.init(projectData as any, DesignerMode.EDIT);
      const project: IProjectInfo = {
        name: "",
        des: "",
        id: importData.id,
        saveType: staticType,
        dataJson: JSON.stringify(projectData),
      };
      await initialization(project);
    };

    // 保存项目
    const saveProject = async () => {
      // 本地保存
      // projectId && doSave(projectId, staticType);
      // 返回到父级
      const projectData = await getProjectData();
      /*  const projectData = await getProjectData();
      const response = await apiFacility.updateDesignJson({
        facilityId: facilityId,
        designerJson: projectData,
      });
      if (response && response.code == 200 && response.data) {
        MessagePlugin.success("保存成功");
      } */
      onSave?.(projectId!, projectData);
    };

    /**
     * 获取项目配置
     * @returns 项目所有配置的json数据
     */
    const getProjectData = async () => {
      const projectData = designerManager.getData();
      const elemConfigs = projectData.layerManager?.elemConfigs;
      const promises: Promise<void>[] = [];
      if (elemConfigs) {
        Object.keys(elemConfigs).forEach((key) => {
          const item = elemConfigs[key];
          if (
            item.base.type === "BaseImage" &&
            (item.style.localUrl as string)?.startsWith("blob")
          ) {
            // 将 blob 数据转换为 base64，并将异步操作添加到 promises 数组中
            promises.push(
              FileUtil.blobToBase64(item.style.localUrl as string).then(
                (res: string | boolean) => {
                  if (res) item.style.localUrl = res;
                  else {
                    console.error(
                      `${item.base.id + "_" + item.base.name} 图片blob转换失败, ${item.style.localUrl}`
                    );
                  }
                }
              )
            );
          }
        });
      }
      await Promise.all(promises);
      return JSON.stringify({
        id: projectId,
        data: projectData,
      });
    };

    // 导出项目
    const exportJson = async () => {
      const timeout = setTimeout(() => {
        getProjectData().then((data) => {
          onExport?.(projectId!, data);
          clearTimeout(timeout);
        });
      }, 500);
    };

    // 导入项目
    const importJson = async () => {
      const input = document.createElement("input");
      input.type = "file";
      input.accept = ".json";
      input.onchange = (e: any) => {
        const file = e.target.files[0] as File;
        if (!file) return;
        file.text().then((fileData: string) => {
          let time = setTimeout(() => {
            loadProject(fileData).then(() => {
              MessagePlugin.success("导入成功");
              clearTimeout(time);
            });
          }, 500);
        });
      };
      input.click();
    };

    const { loaded } = designerManager;
    if (!loaded) return <Loading />;

    return (
      <DesignerProvider
        axios={axios}
        facilityId={facilityId}
        productCodes={productCodes}
      >
        <div style={{ backgroundColor: "#1f1f1f", height: "100%" }}>
          <FrameLayout
            header={
              <DesignerHeader
                onPreview={() => onPreview?.(projectId!, SaveType.LOCAL)}
                onSave={saveProject}
                onExport={exportJson}
                onImport={importJson}
              />
            }
            left={<DesignerLeft />}
            content={<DesignerCanvas />}
            right={<DesignerRight />}
            footer={<DesignerFooter />}
          />
        </div>
      </DesignerProvider>
    );
  }
);

export default observer(Designer);
